/**
 * 变量环境和词法环境
 * let和块级作用域 到底是如何实现的?
 */
function fn() {
  var a = 1;
  let b = 2;
  { // 第一个代码块
    let b = 3;
    var c = 4;
    let d = 5;
    // console.log(a, b, c, d);
  }
  { // 第二个代码块
    let b = 6;
    let d = 7;
    // console.log(a, b, c, d);
  }
}

fn();

/**
 * 先全局下编译，扫描代码
 */
/**
 * 1.全局下编译
 * ES5里面会创建一个VO VariableObject =》 ActivationObject
 * ES6 VariableEnvironment 变量环境 var function + LexicalEnvironment
 */
let globalEc = {
  // this: globalThis, // 代表当前this的指针
  outer: null, // 外部执行上下文环境，相当于实现ES3的scopeChain
  variableEnvironment: {
    fn() {
    }
  }, // 变量环境
  lexicalEnvironment: {} // 词法环境
}

/**
 * 2.编译fn
 */
// 静态作用域, 语法作用域
let fnEc = {
  this: globalThis,
  outer: globalEc, // outer等于声明fn变量的执行上下文环境对象中的变量环境
  variableEnvironment: { // var声明的变量会变量提升
    a: undefined,
    c: undefined
  }, // 变量环境
  lexicalEnvironment: [{ // let声明的
    b: undefined //
  }] // 词法环境
}
/**
 * 3.执行fn
 */
fnEc.variableEnvironment.a = 1;
fnEc.variableEnvironment.b = 2;
// 每当函数执行的时候，遇到一个新的代码块,就会创建一个新的词法环境对象
fnEc.lexicalEnvironment.push({
  b: undefined,
  d: undefined
})
fnEc.lexicalEnvironment[1].b = 3; // 给词法环境变量赋值
fnEc.variableEnvironment.c = 4; // 给variableEnvironment变量环境中的c进行赋值
fnEc.lexicalEnvironment[1].d = 5; // 给词法环境变量赋值

//console.log(a, b, c, d);//1 3 4 5
//var function 放在ve(变量环境) let const 放在le（词法环境）

function getValue(name, ec) {
  // 遍历当前环境下面的词法环境
  for (let i = ec.lexicalEnvironment.length - 1; i >= 0; i--) {
    if (name in ec.lexicalEnvironment[i]) { // 如果在词法环境里面则直接返回
      return ec.lexicalEnvironment[i][name];
    }
  }
  if (name in ec.variableEnvironment) { // 如果在变量环境里面，就返回变量环境里面的
    return ec.variableEnvironment[name];
  }
  if (ec.outer) { // 递归返回父环境的
    return getValue(name, ec.outer);
  }
  return null;
}

console.log(getValue('a', fnEc), getValue('b', fnEc), getValue('c', fnEc), getValue('d', fnEc));

// 遇到第二个代码块
fnEc.lexicalEnvironment.pop(); //将最后一个词法作用域推出
fnEc.lexicalEnvironment.push({
  b: undefined,
  d: undefined
});
fnEc.lexicalEnvironment[1].b = 6;
fnEc.lexicalEnvironment[1].d = 7;
console.log(getValue('a', fnEc), getValue('b', fnEc), getValue('c', fnEc), getValue('d', fnEc));


function two() {
    console.log(a);//
}
function one() {
    var a = 2;
    two();
}
var a = 1;
one(); // 1
let globalEC = {
    ve: {
        two,
        one,
        a
    }
}

let aEc = {
  outer: globalEC,
  ve: {
    a: undefined
  }
}

let twoEc = {
  outer: globalEC,
  ve: {}
}

